use std::collections::{HashMap, HashSet};

use egui::{Rect, Vec2};

use crate::node::{Node, NodeViewer};

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NodeId(pub usize);

impl From<usize> for NodeId {
    fn from(value: usize) -> Self {
        NodeId(value)
    }
}

impl Into<egui::Id> for NodeId {
    fn into(self) -> egui::Id {
        egui::Id::new(self.0)
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Pin {
    In(InPin),
    Out(OutPin)
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct InPin {
    pub(crate) node:NodeId,
    pub(crate) pin:usize
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OutPin {
    pub(crate) node:NodeId,
    pub(crate) pin:usize
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Wire {
    pub(crate) input:InPin,
    pub(crate) output:OutPin
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound(serialize = "T: serde::Serialize", deserialize = "T: serde::Deserialize<'de>")))]
pub(crate) struct NodeData<T> {
    pub node:Node<T>,
    pub id:NodeId,
}

impl <T> NodeData<T> {
    pub fn new(node:Node<T>, id:NodeId) -> Self {
        NodeData {
            node,
            id
        }
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound(serialize = "T: serde::Serialize", deserialize = "T: serde::Deserialize<'de>")))]
pub struct Graph<T> {
    pub(crate) nodes:HashMap<NodeId, NodeData<T>>,
    pub(crate) wires:HashSet<(OutPin, InPin)>,
    pub(crate) last_rect:Option<Rect>,
    id_counter:usize,
    draw_order:Vec<NodeId>,
}

impl <T> Graph<T> {
    pub fn new() -> Self {
        Graph {
            nodes: HashMap::new(),
            wires: HashSet::new(),
            last_rect: None,
            id_counter: 0,
            draw_order: Vec::new(),
        }
    }

    pub fn get_id(&mut self) -> NodeId {
        let id = NodeId(self.id_counter);
        self.id_counter += 1;
        id
    }

    pub fn add_node(&mut self, node:Node<T>) {
        let id = self.get_id();
        // self.nodes.push(NodeData::new(node, id));
        self.nodes.insert(id, NodeData::new(node, id));
        self.draw_order.push(id);
    }

    pub fn get_node<ID:Into<NodeId>>(&self, id:ID) -> Option<&Node<T>> {
        self.nodes.get(&id.into()).map(|data| &data.node)
    }

    pub fn get_node_mut<ID:Into<NodeId>>(&mut self, id:ID) -> Option<&mut Node<T>> {
        self.nodes.get_mut(&id.into()).map(|data| &mut data.node)
    }

    pub fn is_connected(&self, input:InPin, output:OutPin) -> bool {
        self.wires.contains(&(output, input))
    }

    pub fn connect(&mut self, input:InPin, output:OutPin) {
        self.wires.insert((output, input));
    }

    pub fn disconnect(&mut self, input:InPin, output:OutPin) {
        self.wires.remove(&(output, input));
    }

    pub fn get_connected_inputs(&self, output:OutPin) -> Vec<InPin> {
        self.wires.iter().filter_map(|(o, i)| {
            if o == &output {
                Some(*i)
            } else {
                None
            }
        }).collect()
    }

    pub fn get_connected_outputs(&self, input:InPin) -> Vec<OutPin> {
        self.wires.iter().filter_map(|(o, i)| {
            if i == &input {
                Some(*o)
            } else {
                None
            }
        }).collect()
    }

    pub fn get_connected_pins(&self, pin:Pin) -> Vec<Pin> {
        match pin {
            Pin::In(input) => {
                self.get_connected_outputs(input).into_iter().map(Pin::Out).collect()
            },
            Pin::Out(output) => {
                self.get_connected_inputs(output).into_iter().map(Pin::In).collect()
            }
        }
    }

    pub fn nodes(&self) -> Vec<&Node<T>> {
        self.nodes.iter().map(|(_, data)| &data.node).collect()
    }

    // pub fn nodes(&self) -> &Vec<Node<T>> {
    //     &self.nodes
    // }

    pub fn nodes_mut(&mut self) -> Vec<&mut Node<T>> {
        self.nodes.iter_mut().map(|(_, data)| &mut data.node).collect()
    }

    pub fn wires(&self)->Vec<Wire> {
        self.wires.iter().map(|(output, input)| {
            Wire {
                input: *input,
                output: *output
            }
        }).collect()
    }

    pub fn remove_node<ID:Into<NodeId>>(&mut self, id:ID) {
        self.on_remove_node(id);
    }
}

impl <T> Graph<T> {
    pub(crate) fn get_node_data<ID:Into<NodeId>>(&self, id:ID) -> Option<&NodeData<T>> {
        self.nodes.get(&id.into())
    }

    pub(crate) fn get_node_data_mut<ID:Into<NodeId>>(&mut self, id:ID) -> Option<&mut NodeData<T>> {
        self.nodes.get_mut(&id.into())
    }
}

impl <T> Graph<T> {
    fn on_remove_node<ID:Into<NodeId>>(&mut self, id:ID) {
        let id = id.into();
        self.nodes.remove(&id);
        self.wires.retain(|(output, input)| {
            output.node != id && input.node != id
        });
        self.draw_order.retain(|&node_id| node_id != id);
    }
}

impl <T> Graph<T> where T:PartialEq {
    pub fn remove_node_by_data(&mut self, data:T) {
        let Some(id) = self.search_node_by_data(data) else {
            return;
        };
        self.on_remove_node(id);
    }

    pub fn search_node_by_data(&self, data:T) -> Option<NodeId> {
        self.nodes.iter().find(|(_, tdata)| tdata.node.data() == &data).map(|(id, _)| *id)
    }
}

pub trait GraphViewer<T> {
    //fn draw_node(&self, index:usize, node:&mut Node<T>, ui:&mut egui::Ui);
    //fn draw_wire(&self, wire:&Wire, nodes:&Vec<Node<T>>, ui:&mut egui::Ui);
    //...
    fn draw_background(&mut self, graph:&mut Graph<T>, ui:&mut egui::Ui, rect:egui::Rect) {
        //创建红色文本的Hello World Label

        //填充背景色
        if let Some(rect) = graph.last_rect {
            let painter = ui.painter();
            //给Rect再增加几百像素，如果大小正好那么拖动的时候很可能会出现空白
            let rect = rect.expand(100.);
            painter.rect_filled(rect, 0., egui::Color32::from_rgb(110, 110, 110));
        }

        ui.colored_label(egui::Color32::RED, "Hello World");
    }

    fn draw_nodes(&mut self, graph:&mut Graph<T>, ui:&mut egui::Ui, node_viewer:&mut dyn NodeViewer<T>) {
        //绘制节点,nodeId实现了copy，所以可以直接传递
        let draw_order = graph.draw_order.clone();
        let mut moved_nodes = Vec::new();
        let mut top_node = None;
        for node_id in draw_order {
            let (move_info, top) = self.draw_node(node_id, graph, ui, node_viewer);
            if let Some(move_info) = move_info {
                moved_nodes.push(move_info);
            }
            if top {
                top_node = Some(node_id);
            }
        }

        ui.ctx().request_repaint();

        if let Some(top_node) = top_node {
            //将top_node移动到draw_order的最后面
            graph.draw_order.retain(|&node_id| node_id != top_node);
            graph.draw_order.push(top_node);
        }

        ui.ctx().request_repaint();

        for (node_id, delta) in moved_nodes {
            if let Some(node_data) = graph.get_node_data_mut(node_id) {
                node_data.node.set_position(node_data.node.position() + delta);
            }
        }

        ui.ctx().request_repaint();

    }

    fn draw_node(&mut self, node_id:NodeId, graph:&mut Graph<T>, ui:&mut egui::Ui, node_viewer:&mut dyn NodeViewer<T>)->DrawNodeResult{
        let Some(node_data) = graph.get_node_data_mut(node_id) else {
            return (None, false);
        };

        let rect = Rect::from_min_size(node_data.node.position, node_data.node.size);
        let mut move_info = None;
        let response = ui.interact(rect, ui.id().with(format!("frame{}", node_id.0)), egui::Sense::click_and_drag());
        let drag_delta = response.drag_delta();
        if drag_delta != Vec2::ZERO {
            // node_data.node.set_position(node_data.node.position + response.drag_delta());
            move_info = Some((node_id, response.drag_delta()));
        }

        let node_ui = &mut ui.new_child(
            egui::UiBuilder::new()
                .max_rect(rect)
                .layout(egui::Layout::top_down(egui::Align::Center))
                .id_salt(node_id),
        );

        let rect = node_viewer.draw(&mut node_data.node, node_ui);

        let size = rect.size();
        node_ui.set_min_size(size);
        node_data.node.size = size;
        
        (move_info, response.dragged() || response.clicked())

    }
}

type MoveInfo = (NodeId,Vec2);

type DrawNodeResult = (Option<MoveInfo>, bool);
